﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using Microsoft.SqlServer.Dts.Pipeline;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Pipeline.Design;
using Microsoft.SqlServer.Dts.Runtime;
using Microsoft.SqlServer.Dts.Design;

namespace Pestre.SqlServer.SSIS.DataFlowComponent
{
    /*
     * ErrorOutputDescriptionUI represents ErrorOutputDescription component's User Interface.
     * It provides a window form for user to edit the properties of the component.
     * 
     * Also, it's the only class which can iterates through all components of the dataflow thanks
     * to its method "Initialize". This method stores information on all columns of the dataflow.
     */
    public class ErrorOutputDescriptionUI90 : IDtsComponentUI
    {
        IDTSComponentMetaData90        _md;
        IServiceProvider               _sp;
        IDtsPipelineEnvironmentService _pipelineService;

        /*
         * This function is called when the developper click on "Edit".
         * A new window form is opened in which user can edit the properties of the component.
         */
        public bool Edit(IWin32Window parentWindow, Variables vars, Connections cons)
        {
            // Create the component's form, and then display it.
            ErrorOutputDescriptionUIForm90 componentEditor = new ErrorOutputDescriptionUIForm90(_md);
            DialogResult result = componentEditor.ShowDialog(parentWindow);
            if (result == DialogResult.OK)
                return true;
            return false;
        }

        /*
         * This method is called at the component creation and each time it is edited.
         *   - Store access to the metadata of the current dataflow.
         *   - Store information on all column of the dataflow.
         */
        public void Initialize(IDTSComponentMetaData90 dtsComponentMetadata, IServiceProvider serviceProvider)
        {
            // Store the component metadata.
            _md = dtsComponentMetadata;
            _sp = serviceProvider;
            _pipelineService = (IDtsPipelineEnvironmentService)serviceProvider.GetService(typeof(IDtsPipelineEnvironmentService));

            // Get All column LineageID and store it in custom property
            SetLineageInformation();

            // Desactivates the warning used to ask user to reinitialiaze Lineage information.
            _md.CustomPropertyCollection[ErrorOutputDescription.PROP_SHOW_WARNING].Value = false;
        }

        /*
         * Display Help message.
         */
        public void Help(IWin32Window parentWindow)
        {
            MessageBox.Show("This component provides additional information on error.");
        }

        /*
         * Do nothing.
         */
        public void New(IWin32Window parentWindow)
        {
        }

        /*
         * Do nothing.
         */
        public void Delete(IWin32Window parentWindow)
        {
        }

        /*
         * When an error occured, SSIS notices the dataflow with the LineageID of the impacted column.
         * This method retrieve all LineageID of the dataflow with some information. These data will
         * be used during Running Time to generated error description.
         * 
         * The retrieved data are stored in a custom property.
         * 
         * It seems that some components don't provide 'LineageID' in 'ErrorColumn' but 'ID'.
         * To resolve this problem ID and LineageID are retrieved in the following order : 
         *   - Step 1 : Retrieve ID of direct upstream components
         *   - Step 2 : Retrieve all existing LineageID (Except those generated by our component)
         *   - Step 3 : Retrieve all existing ID (Except those generated by our component)
         */
        private void SetLineageInformation()
        {
            // Get DataFlow object from pipelineService
            MainPipe task = (MainPipe)_pipelineService.PipelineTaskHost.InnerObject;
            // Array for ColumnInfo instances
            ArrayList tabLineage = new ArrayList();

            // Step 1 : Retrieve all column's ID of direct upstream components if they are Destination Components
            for (int iPath = 0; iPath < task.PathCollection.Count; iPath++)
                if (task.PathCollection[iPath].EndPoint.Component.ID == _md.ID) // This path is connected to our component
                {
                    IDTSComponentMetaData90 mdUpstream = task.PathCollection[iPath].StartPoint.Component; // Upstream Component

                    if ((int)mdUpstream.ObjectType == 10) // Output Components have ObjectType set to 10
                    {
                        // Retrieve Input columns
                        foreach (IDTSInput90 input in mdUpstream.InputCollection)
                            foreach (IDTSInputColumn90 col in input.InputColumnCollection)
                            {
                                if (!col.IsValid)
                                    continue;
                                bool bAlreadyExist = false;
                                foreach (ColumnInfo90 ci in tabLineage)
                                    if (ci.LineageID == col.ID)
                                    {
                                        bAlreadyExist = true;
                                        break;
                                    }
                                if (bAlreadyExist == false)
                                    tabLineage.Add(new ColumnInfo90(col.ID, col.Name, col.DataType.ToString(), col.Length));
                            }
                    }
                }

            // Step 2 : Iterate through all components to retrieve all existing LineageID
            foreach (IDTSComponentMetaData90 md in task.ComponentMetaDataCollection)
            {
                if (md.ID == _md.ID) // Don't parse our component
                    continue;
                // Retrieve Input columns
                foreach (IDTSInput90 input in md.InputCollection)
                    foreach (IDTSInputColumn90 col in input.InputColumnCollection)
                    {
                        if (!col.IsValid)
                            continue;
                        bool bAlreadyExist = false;
                        foreach (ColumnInfo90 ci in tabLineage)
                            if (ci.LineageID == col.LineageID)
                            {
                                bAlreadyExist = true;
                                break;
                            }
                        if (bAlreadyExist == false)
                            tabLineage.Add(new ColumnInfo90(col.LineageID, col.Name, col.DataType.ToString(), col.Length));
                    }
                // Retrive Output columns
                foreach (IDTSOutput90 output in md.OutputCollection)
                    foreach (IDTSOutputColumn90 col in output.OutputColumnCollection)
                    {
                        bool bAlreadyExist = false;
                        foreach (ColumnInfo90 ci in tabLineage)
                            if (ci.LineageID == col.LineageID)
                            {
                                bAlreadyExist = true;
                                break;
                            }
                        if (bAlreadyExist == false)
                            tabLineage.Add(new ColumnInfo90(col.LineageID, col.Name, col.DataType.ToString(), col.Length));
                    }
            }

            // Step 3 : Iterate through all components to retrieve all existing ID
            foreach (IDTSComponentMetaData90 md in task.ComponentMetaDataCollection)
            {
                if (md.ID == _md.ID) // Don't parse our component
                    continue;
                // Retrieve Input columns
                foreach (IDTSInput90 input in md.InputCollection)
                    foreach (IDTSInputColumn90 col in input.InputColumnCollection)
                    {
                        if (!col.IsValid)
                            continue;
                        bool bAlreadyExist = false;
                        foreach (ColumnInfo90 ci in tabLineage)
                            if (ci.LineageID == col.ID)
                            {
                                bAlreadyExist = true;
                                break;
                            }
                        if (bAlreadyExist == false)
                            tabLineage.Add(new ColumnInfo90(col.ID, col.Name, col.DataType.ToString(), col.Length));
                    }
                // Retrive Output columns
                foreach (IDTSOutput90 output in md.OutputCollection)
                    foreach (IDTSOutputColumn90 col in output.OutputColumnCollection)
                    {
                        bool bAlreadyExist = false;
                        foreach (ColumnInfo90 ci in tabLineage)
                            if (ci.LineageID == col.ID)
                            {
                                bAlreadyExist = true;
                                break;
                            }
                        if (bAlreadyExist == false)
                            tabLineage.Add(new ColumnInfo90(col.ID, col.Name, col.DataType.ToString(), col.Length));
                    }
            }

            // Save Columns Information into custom property
            String strLineage = "";
            for (int i = 0; i < tabLineage.Count; i++)
                strLineage = strLineage + tabLineage[i].ToString();
            _md.CustomPropertyCollection[ErrorOutputDescription.PROP_LINEAGE_ID].Value = strLineage;
        }
    }
}
